<!--
 虚拟列表
 @Author: mosowe
 @Date:2023-03-07 09:14:40
-->

<template>
  <view class="zo-invented-list">
    <scroll-view
      class="container"
      scroll-y
      :style="{ height: boxHeight + 'px' }"
      @scroll="handleScroll">
      <div
        class="zo-invented-wrap"
        :style="{ height: itemHeight * props.list.length + 'px' }">
        <div
          class="zo-invented-content"
          :style="{ transform: 'translateY(' + offsetY + 'px)' }">
          <view
            class="zo-invented-item"
            v-for="(item, index) in showList"
            :key="index">
            <slot :item="item"></slot>
          </view>
        </div>
      </div>
    </scroll-view>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick, getCurrentInstance, computed } from 'vue';
// props定义
interface PropsType {
  list: any[]; // 列表
}
const props = defineProps<PropsType>();

// emits
interface EmitsType {
  (e: 'scroll', data: any): void; // 选中时触发事件
}
const emits = defineEmits<EmitsType>();

const instance = getCurrentInstance();

const showList = ref<any>([]); // 展示的数据列表
const boxHeight = ref(0); // 组件可视区高度
const itemHeight = ref(0); // 每条数据高度，计算第一条数据的高度，以第一条数据高度为主
const cacheNum = 5; // 前后缓存数目
const pageNum = computed(() => {
  if (boxHeight.value && itemHeight.value) {
    return Math.ceil(boxHeight.value / itemHeight.value) + cacheNum;
  } else {
    return 50;
  }
});

onMounted(() => {
  nextTick(() => {
    const query = uni.createSelectorQuery().in(instance);
    query
      .select('.zo-invented-list')
      .boundingClientRect((res: any) => {
        boxHeight.value = Math.floor(res?.height) || 0;
      })
      .select('.zo-invented-item')
      .boundingClientRect((res: any) => {
        itemHeight.value = Math.floor(res?.height) || 0;
      })
      .exec();
  });
});

// 滚动触发
const offsetY = ref(0);
const handleScroll = (e: any) => {
  const scrollTop = Math.floor(e.detail.scrollTop);

  offsetY.value = scrollTop - (scrollTop % itemHeight.value);

  let startIndex = Math.floor(scrollTop / itemHeight.value);

  if (startIndex > cacheNum) {
    offsetY.value -= cacheNum * itemHeight.value;
    startIndex = startIndex - cacheNum;
  }

  setShowList(startIndex);

  emits('scroll', scrollTop);
};
// 配置showList
const setShowList = (startIndex: number) => {
  showList.value = props.list.slice(startIndex, startIndex + pageNum.value);
};
setShowList(0);
</script>

<style lang="scss" scoped>
.zo-invented-list {
  overflow: hidden;
  height: 100%;
  width: 100%;
}
</style>
